# -*- coding: utf-8 -*-

import copy
import os
from scrapy.selector import Selector

from spidertools.utils.xpath_utils import get_alltext, get_all_tables
from spidertools.utils.text_utils import replace_punctuation, clean_html_whitesplace
from spidertools.utils.snippets import combine_two_dict

from info_fsm import InfoMachine
from configs.commom import notice_style_dic
from utils.logger_utils import savelog, LogStateString
from utils.check_utils import check_json, save_check_result_to_file, translatedict
from utils.extractor_utils import get_no_needcheck_urls, get_extend_keywords
from utils.xpath_utils import get_alltext_exclude_table
from table_info_extract import dict_mapping_triedTree
from table_info_extract.table_fsm import TableExtractMachine


class BaseExtractor(object):
    def __init__(self, info_dict, one_line_mode_return_result=True):
        '''
        info_dict,从mongo数据库里面拿到的dict对象
        '''
        self.info_dict = copy.deepcopy(info_dict)
        self.html = info_dict['html']
        self.sel = Selector(text=self.html)
        self.base_pattern = '%s:\s*(.*)'
        if 'html' in self.info_dict:
            del self.info_dict['html']
        if '_id' in self.info_dict:
            del self.info_dict['_id']

        self.no_needcheck_list = get_no_needcheck_urls(self.info_dict['province'], self.info_dict["source_type"])
        self.extend_keywords = get_extend_keywords(self.info_dict['province'], self.info_dict["source_type"])

        self.table_extract_machine = TableExtractMachine(one_line_mode_return_result=one_line_mode_return_result,extend_keywords=self.extend_keywords)
        self.texts_extract_machine = InfoMachine(self.base_pattern, extend_keywords=self.extend_keywords)

    def texts_matchine_parse(self, texts):
        '''
        解析文本列表texts里面的内容
        '''

        clean_texts = []
        # 清理文本中的一些空几个，不可见字符
        for text in texts:
            text = replace_punctuation(text.strip())
            text = self.common_replace_word(text)
            clean_texts.append(text)

        self.merge_next_text(clean_texts)
        output_dict = self.texts_extract_machine.run_list(clean_texts)
        return output_dict

    def merge_next_text(self,textlists):
        '''如果list里面存在以冒号结尾的。则吧下一行复制上去'''
        for index,text in enumerate(textlists[:-1]):
            if text.endswith(":"):
                textlists[index] = textlists[index]+ textlists[index+1]

    def common_text_parse(self, xpath='//body',with_table_html=True):
        '''通用文本解析，解析body下面的所有文本'''
        # 获取招标公告正文
        content_root_nodes = self.sel.xpath(xpath)
        texts = []
        # 遍历所有子节点，获取相应的文本
        for node in content_root_nodes:
            if with_table_html:
                node_text = get_alltext(node)
            else:
                node_text = get_alltext_exclude_table(node)
            texts.append(node_text)

        output_dict = self.texts_matchine_parse(texts)
        return output_dict

    def common_table_parse(self):
        '''通用表格解析，解析网页里面的所有表格内容'''
        output_dict = {}
        tables = get_all_tables(self.sel)
        for table in tables:
            result = self.table_extract_machine.run_table(table)
            output_dict = combine_two_dict(output_dict, result)
        return output_dict

    def commom_announcement_parse(self):
        '''
        通用网页公告解析
        :return:
        '''

        text_parsed_dicts = self.common_text_parse()
        table_parsed_dicts = self.common_table_parse()
        output = {}
        if text_parsed_dicts:
            output = combine_two_dict(output, text_parsed_dicts)
        if table_parsed_dicts:
            output = combine_two_dict(output, table_parsed_dicts)

        return output

    def common_replace_word(self,text):
        return text

    def start_parse(self):
        '''
        如果通用提起不能完全提取出来，则自己根据自己的需求，在写几个分支
        '''

        '''
                如果通用提起不能完全提取出来，则自己根据自己的需求，在写几个分支
                '''
        ## 第一步，如果判断网页没有有效内容,直接返回一个包含not_need_checkjson的dict，跳过
        if not self.checkIfNeedParse():
            savelog(LogStateString.Waring_NoValidConent, msg_dict=self.info_dict)
            return {"not_need_checkjson": '1'}

        ##  第二步： 判断是否是要解析的公告类型，如果是，记录日志并直接返回
        if self.check_purchase():
            savelog(LogStateString.Waring_NoNeedParse, msg_dict={"url": self.info_dict["origin_url"]})
            return {"not_need_checkjson": '1'}

        ## 第二步，是不是有一些需要特殊处理的操作，比如 替换某一些词条
        self.common_replace_html()

        ## 第三步，解析
        result = self.parse_announcement_info()
        if not result:
            result = self.commom_announcement_parse()

        ## 第四步，一些缺失信息的特殊获取
        result = self.get_infos_from_otherway(result, self.html)

        ## 第五步，对一些字段做简单的清晰
        result = self.clean_dict_keys(result)
        return result

    def combine_info_and_check(self, result, combine_info):
        result = combine_two_dict(result, combine_info)
        ##  5.调用check_json，检查result是否正常
        translatedict(result)
        log_code_dict = check_json(result)
        save_check_result_to_file(log_code_dict, result, self.info_dict)
        if LogStateString.FatalError_LockKeyWords in log_code_dict.keys() or \
                LogStateString.FatalError_TypeNotList in log_code_dict.keys():
            result = None
        return result

    def output_extractor_dict(self):
        '''
        输出最终的提取结果
        '''
        result = None
        ##   1. 判断解析的url是否在no_needcheck_list里面，如果是，直接返回
        if self.info_dict["origin_url"] in self.no_needcheck_list:
            savelog(LogStateString.Waring_NoNeedCheckUrl, msg_dict={"url": self.info_dict["origin_url"]})
            return None

        ##   2. 判断公告类型是不是支持解析，不支持直接返回
        clean_announcement = clean_html_whitesplace(self.info_dict['公告类型'])
        if clean_announcement not in notice_style_dic:
            return None

        ##  3.解析并和原始的info_dict里面的内容进行合并
        self.info_dict['公告类型'] = notice_style_dic.get(clean_html_whitesplace(clean_announcement))

        extractor_output = self.start_parse()

        ## 4.与其他平台重复的数据 或者有 not_need_checkjson,则跳过checkjson，直接return
        if type(extractor_output) == dict and (extractor_output.get("Repeat_information") or extractor_output.get("not_need_checkjson")):
            return None

        ## 5.合并info_dict以及
        if extractor_output:
            html_info_dict = dict_mapping_triedTree(self.info_dict)
            if type(extractor_output) == list:
                result = []
                for index, value in enumerate(extractor_output):
                    temp_result = self.combine_info_and_check(value, html_info_dict)
                    if temp_result:
                        result.append(temp_result)
            else:
                result = self.combine_info_and_check(extractor_output, html_info_dict)

        return result

    def checkIfNeedParse(self):
        '''
        判断是不是需要走解析
        return True就是需要
        return false就是不需要
        '''
        return True

    def common_replace_html(self):
        '''
        对网页源码进行一些特殊处理，并需要更新self.sel对象
        '''
        pass


    def parse_announcement_info(self):
        '''
        各自去根据各种网页情况，写分支处理。。
        如果返回为None或者{}，则会去调用commom_announcement_parse走通用解析
        '''
        return {}

    def get_infos_from_otherway(self, result, html=None):
        '''
        对提取结果进行一些变换；比如没有项目名称，需要从标段名称里面提取
        或者从标题里面获取项目名称
        '''
        return result

    def clean_dict_keys(self, result):
        '''
        对result里面的一些字段进行简单的处理，比如清洗掉无用字符等
        '''
        return result

    def check_purchase(self):
        """
            判断是否是需要解析的，如果是，return True，可以重写该方法
            通过标题和正文结合来看是否是采购公告
        :param check_text:html正文
        :param announcement_title:公告标题
        :return:    True:不需要需要解析          False:需要解析
        """
        return_flag = False
        return return_flag


